package com.azhe.myframework.servlet.v2;

import com.azhe.myframework.annotation.*;
import org.springframework.util.CollectionUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * Description:
 *
 * @author Linzr
 * @version V1.0.0
 * @date 2021/1/8 11:18 上午
 * @since V1.0.0
 */
public class MyDispatcherServlet extends HttpServlet {
    // 接收配置文件application.properties
    Properties properties = new Properties();

    // 保存需要实例化并装进ioc的类名
    private final List<String> fileNameList = new ArrayList<>();

    // ioc容器
    private final Map<String, Object> ioc = new HashMap<>();

    // 封装url和对应的具体方法的容器
    private final List<HandlerMapping> handlerMappingList = new ArrayList<>();

    /**
     * 初始化 模板方法模式
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 加载配置
        doLoadConfig(config.getInitParameter("contextConfigLocation"));
        // 扫描包路径下的所有class类
        doScanner(properties.getProperty("scanPackage"));
        // 初始化bean装载ioc
        initIoc();
        // 自动依赖注入
        doAutowired();
        // 初始化HandlerMapping
        initHandlerMapping();
    }
    private void doLoadConfig(String config) {
        try {
            // 将配置文件转成输入流并对接上Properties属性
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(config);
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void doScanner(String basePath) {
        URL baseResource = this.getClass().getClassLoader().getResource(basePath.replaceAll("\\.", "/"));
        // 获得basePath对应的文件/文件夹
        File baseFile = new File(baseResource.getFile());
        // 遍历basePath下的所有文件
        for (File file : baseFile.listFiles()) {
            if (file.isDirectory()) {
                doScanner(basePath + "." + file.getName());
            } else if (file.getName().endsWith(".class")) {
                fileNameList.add(basePath + "." + file.getName().replace(".class", ""));
            }
        }
    }

    private void initIoc() {
        try {
            if (fileNameList.isEmpty()) { return;}
            for (String fileName : fileNameList) {
                // 获得bean类信息
                Class<?> clazz = Class.forName(fileName);
                instanceFile(clazz);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doAutowired() {
        try {
            // 遍历ioc容器内的所有bean
            for (Object bean : ioc.values()) {
                // 遍历bean下的所有属性
                for (Field field : bean.getClass().getDeclaredFields()) {
                    if (!field.isAnnotationPresent(MyAutowired.class)) {continue;}

                    MyAutowired annotation = field.getAnnotation(MyAutowired.class);
                    String beanName = !"".equals(annotation.value())? annotation.value(): toLowFirstChar(field.getType().getSimpleName());
                    field.setAccessible(true);
                    field.set(bean, ioc.get(beanName));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void initHandlerMapping() {
        for (Object value : ioc.values()) {
            Class<?> clazz = value.getClass();
            if (!clazz.isAnnotationPresent(MyController.class)) {
                continue;
            }
            // 获取通用路径
            String baseUrl = "/";
            if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
                MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
                baseUrl += annotation.value();
            }
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
                String url = (baseUrl + "/" + annotation.value()).replaceAll("/+", "/");
                handlerMappingList.add(new HandlerMapping(url, value, method));
            }
        }
        System.out.println("handlerMapping had init,size:" + handlerMappingList.size());
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatcher(req, resp);
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }


    /**
     * 处理请求
     * @param req
     * @param resp
     */
    private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) {
        try {
            String url = req.getRequestURI().replaceAll(req.getContextPath(), "");
            HandlerMapping handlerMapping = getHandler(url);
            if (handlerMapping == null) {
                resp.getWriter().write("404 not found");
                return;
            }

            // 填充实参
            Method method = handlerMapping.method;
            Object[] params = new Object[method.getParameterCount()];
            Map<String, String[]> parameterMap = req.getParameterMap();
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (String name : parameterMap.keySet()) {
                if (!handlerMapping.paramIndexMap.containsKey(name)) {
                    continue;
                }
                Integer index = handlerMapping.paramIndexMap.get(name);
                params[index] = coverParam(parameterTypes[index], parameterMap.get(name));
            }

            // 反射调用方法
            Object invoke = method.invoke(handlerMapping.controller, params);
            if (null == invoke || Void.class == method.getReturnType()) {
                return;
            }
            resp.getWriter().write(invoke.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 实例化bean
     * @param clazz
     */
    private void instanceFile(Class<?> clazz) throws Exception {
        // MyController 注解
        if (clazz.isAnnotationPresent(MyController.class)) {
            ioc.put(toLowFirstChar(clazz.getSimpleName()), clazz.newInstance());
            return;
        }
        // MyService 注解
        if (clazz.isAnnotationPresent(MyService.class)) {
            Object bean = clazz.newInstance();
            MyService annotation = clazz.getAnnotation(MyService.class);
            String beanName = annotation.value();
            if ("".equals(beanName.trim())) {
                beanName = toLowFirstChar(clazz.getSimpleName());
            }
            ioc.put(beanName, bean);

            // 将接口类型也装备进ioc
            Class<?>[] interfaces = clazz.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                if (ioc.containsKey(anInterface.getSimpleName())) {
                    continue;
                }
                ioc.put(toLowFirstChar(anInterface.getSimpleName()), bean);
            }
        }
    }

    private String toLowFirstChar(String content) {
        char[] chars = content.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    // 将请求数据转换为对应方法需要的类型
    private Object coverParam(Class<?> parameterType, String[] content) {
        if (parameterType == Integer.class) {
            int result = 0;
            for (String num : content) {
                result += Integer.parseInt(num);
            }
            return result;
        }
        if (parameterType == String.class) {
            return Arrays.toString(content).replaceAll("[\\[\\]]", "").replaceAll("s+", ",");
        }
        if (parameterType == Double.class) {
            double result = 0;
            for (String num : content) {
                result += Double.parseDouble(num);
            }
            return result;
        }
        return null;
    }

    // 获取对应的handlerMapping
    private HandlerMapping getHandler(String url) {
        if (CollectionUtils.isEmpty(handlerMappingList)) {
            return null;
        }
        for (HandlerMapping handlerMapping : handlerMappingList) {
            if (url.equals(handlerMapping.url)) {
                return handlerMapping;
            }
        }
        return null;
    }

    // 存储url和对应的对象方法的一个静态内部类
    private class HandlerMapping {
        private String url;   // url路径
        private Object controller;   // 对应的controller
        private Method method;   // 对应的方法
        private Map<String, Integer> paramIndexMap;  // 参数名称和对应的角标

        public HandlerMapping(String url, Object controller, Method method) {
            this.url = url;
            this.controller = controller;
            this.method = method;
            initParamIndexMap(method);
        }

        // 初始化
        private void initParamIndexMap(Method method) {
            paramIndexMap = new HashMap<>(method.getParameterCount());
            Annotation[][] annotations = method.getParameterAnnotations();
            for (int i = 0; i < method.getParameterCount(); i++) {
                for (Annotation annotation : annotations[i]) {
                    if (!(annotation instanceof MyRequestParam)) {
                        continue;
                    }
                    paramIndexMap.put(((MyRequestParam) annotation).value(), i);
                }
            }
        }
    }

}
